﻿namespace QdArch
{
    public class SlabAttribute : CptAttribute
    {
        public SlabAttribute(string category, string subCategorys) : base(category, subCategorys)
        {

        }
        public const string BuiltinUuid = "{3402A47A-B4CA-35D9-3084-BD6013238CAC}";
        public override LcComponentDefinition GetBuiltinCpt(string subCategory)
        {
            return new QdSlabDef(BuiltinUuid, "内置", subCategory);
        }
    }

    [SlabAttribute("板", "普通板")]
    public class QdSlabDef : LcComponentDefinition
    {
        static QdSlabDef()
        {
            CptTypeParamsDef<QdSlabDef>.ParamsDef = new ParameterSetDefinition {
                new ParameterDefinition { DataType = LcDataType.String, Name = "MaterialId", DisplayName = "材质", },
            };
        }
        public QdSlabDef(string uuid, string name, string subCategory)
            : base(uuid, name, "板", subCategory, new LcParameterSet(CptTypeParamsDef<QdSlabDef>.ParamsDef))
        {
            this.Parameters = new ParameterSetDefinition
            {
                new ParameterDefinition(){ Name = "Thickness",DisplayName="板厚", DataType=LcDataType.Double },
                new ParameterDefinition(){ Name = "Bottom",DisplayName="底高度", DataType=LcDataType.Double },
            };
            var mat = MaterialManager.DefaultMat;
            if (subCategory == "砌体墙")
                mat = MaterialManager.GetMaterial(MaterialManager.MasonryUuid);
            else if (subCategory == "混凝土墙")
                mat = MaterialManager.GetMaterial(MaterialManager.ConcreteUuid);
            this.Solid3dProvider = new Solid3dProvider(name) { AssignMaterialsFunc = (c, s) => new LcMaterial[] { mat } };
        }
    }


    public class QdSlab : DirectComponent
    {
        public List<Vector2> Outline { get; set; }

        public List<List<Vector2>> Holes { get; set; }

        public List<QdUpDownSlab> UpSlabs { get; set; }
        public List<QdUpDownSlab> DownSlabs { get; set; }
        public double Thickness
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Thickness"]);
            }
            set
            {
                this.Parameters["Thickness"] = value;
            }
        }
        public double Bottom
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Bottom"]);
            }
            set
            {
                this.Parameters["Bottom"] = value;
            }
        }
        public Vector2 DragPoint { get; set; }
        public QdSlab(QdSlabDef slabDef):base(slabDef)
        {
            this.Type = ArchElementType.Slab;
            this.Outline = new List<Vector2>();
            this.Holes = new List<List<Vector2>>();
        }
        public override Box2 GetBoundingBox()
        {
            return new Box2().ExpandByPoints(this.Outline.ToArray());
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                //如果元素盒子，与多边形盒子不相交，那就可能不相交
                return false;
            }

            var isIntersect = LcGeoUtils.IsPolygonIntersectPolygon(this.Outline.ToArray(), testPoly.Points);
            if (isIntersect)
            {
                return true;
            }

            foreach (var item in this.Holes)
            {
                isIntersect = LcGeoUtils.IsPolygonIntersectPolygon(item.ToArray(), testPoly.Points);
                if (isIntersect)
                {
                    return true;
                }
            }
            return false;
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            var thisBox = this.BoundingBox;
            if (!thisBox.IntersectsBox(testPoly.BoundingBox))
            {
                return false;
            }

            return GeoUtils.IsPolygonContainsPolygon(this.Outline.ToArray(), testPoly.Points);

        }
        public void TranslationByOffset(Vector2 offset)
        {
            this.DragPoint += offset;
            for (int i = 0; i < this.Outline.Count; i++)
            {
                this.Outline[i] += offset;
            }

            foreach (var hole in this.Holes)
            {
                for (int i = 0; i < hole.Count; i++)
                {
                    hole[i] += offset;
                }
            }

            foreach (var item in this.UpSlabs)
            {
                item.TranslationByOffset(offset);
            }

            foreach (var item in this.DownSlabs)
            {
                item.TranslationByOffset(offset);
            }
        }

        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteProperties(writer, soptions);
            writer.WriteNumberProperty(nameof(Thickness), this.Thickness);
            writer.WriteNumberProperty(nameof(Bottom), this.Bottom);
            writer.WriteCollectionProperty(nameof(Outline), this.Outline, soptions);
            writer.WriteCollectionProperty(nameof(this.Holes), this.Holes, soptions);
            writer.WritePropertyName(nameof(this.UpSlabs));
            writer.WriteStartArray();
            foreach (QdUpDownSlab item in this.UpSlabs)
            {
                item.WriteProperties(writer, soptions);
            }
            writer.WriteEndArray();

            writer.WritePropertyName(nameof(this.DownSlabs));
            writer.WriteStartArray();
            foreach (QdUpDownSlab item in this.DownSlabs)
            {
                item.WriteProperties(writer, soptions);
            }
            writer.WriteEndArray();

        }

        public override void ReadProperties(ref JsonElement jele)
        {
            base.ReadProperties(ref jele);

            this.Bottom = jele.ReadDoubleProperty(nameof(this.Bottom));
            this.Thickness = jele.ReadDoubleProperty(nameof(this.Thickness));
            this.Outline = (List<Vector2>)jele.ReadListProperty<Vector2>(nameof(this.Outline));
            this.Holes = (List<List<Vector2>>)jele.ReadListProperty<List<Vector2>>(nameof(this.Holes)) ?? new List<List<Vector2>>();
            if (jele.TryGetProperty(nameof(this.UpSlabs), out JsonElement _))
            {
                this.UpSlabs = QdUpDownSlab.ReadUpDownSlabSetObject(ref jele, nameof(this.UpSlabs), this);
            }
            if (jele.TryGetProperty(nameof(this.DownSlabs), out JsonElement _))
            {
                this.DownSlabs = QdUpDownSlab.ReadUpDownSlabSetObject(ref jele, nameof(this.DownSlabs), this);
            }
        }
    }

    public class QdUpDownSlab
    {
        public QdSlab Host { get; set; }
        public double Bottom { get; set; }
        public double Thickness { get; set; }
        public List<Vector2> Outline { get; set; }  = new List<Vector2>();
        public List<List<Vector2>> Holes { get; set; } = new List<List<Vector2>>();
        public List<QdUpDownSlab> UpSlabs { get; set; } = new List<QdUpDownSlab>();
        public List<QdUpDownSlab> DownSlabs { get; set; } = new List<QdUpDownSlab>();
        public QdUpDownSlab()
        {
        }
        public QdUpDownSlab(List<Vector2> outline)
        {
            this.Outline = outline;
        }


        public void TranslationByOffset(Vector2 offset)
        {
            for (int i = 0; i < this.Outline.Count; i++)
            {
                this.Outline[i] += offset;
            }

            foreach (var hole in this.Holes)
            {
                for (int i = 0; i < hole.Count; i++)
                {
                    hole[i] += offset;
                }
            }

            foreach (var item in this.UpSlabs)
            {
                item.TranslationByOffset(offset);
            }

            foreach (var item in this.DownSlabs)
            {
                item.TranslationByOffset(offset);
            }
        }

        public void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            writer.WriteStartObject();
            writer.WriteNumberProperty(nameof(this.Bottom), this.Bottom);
            writer.WriteNumberProperty(nameof(this.Thickness), this.Thickness);

            writer.WriteCollectionProperty(nameof(Outline), this.Outline, soptions);
            writer.WriteCollectionProperty(nameof(this.Holes), this.Holes, soptions);

            writer.WritePropertyName(nameof(this.UpSlabs));
            writer.WriteStartArray();
            foreach (QdUpDownSlab item in this.UpSlabs)
            {
                item.WriteProperties(writer, soptions);
            }
            writer.WriteEndArray();

            writer.WritePropertyName(nameof(this.DownSlabs));
            writer.WriteStartArray();
            foreach (QdUpDownSlab item in this.DownSlabs)
            {
                item.WriteProperties(writer, soptions);
            }
            writer.WriteEndArray();

            writer.WriteEndObject();
        }

        public void ReadProperties(ref JsonElement jele)
        {
            //this.Host
            this.Bottom = jele.ReadDoubleProperty(nameof(this.Bottom));
            this.Thickness = jele.ReadDoubleProperty(nameof(this.Thickness));
            this.Outline = (List<Vector2>)jele.ReadListProperty<Vector2>(nameof(this.Outline));
            this.Holes = (List<List<Vector2>>)jele.ReadListProperty<List<Vector2>>(nameof(this.Holes));

            if (jele.TryGetProperty(nameof(this.UpSlabs), out JsonElement upJsonEle))
            {
                //嵌套升降板
            }
            if (jele.TryGetProperty(nameof(this.DownSlabs), out JsonElement downJsonEle))
            {
                //嵌套升降板
            }
        }

        public static List<QdUpDownSlab> ReadUpDownSlabSetObject(ref JsonElement jele, string name, QdSlab host)
        {
            var exist = jele.TryGetProperty(name, out JsonElement prop);
            if (!exist || prop.NullOrUndefined()) return new List<QdUpDownSlab>();

            var arr = prop.EnumerateArray().ToArray();
            var upDownSlabs = new List<QdUpDownSlab>();
            for (var i = 0; i < arr.Length; i++)
            {
                var upDownSlab = new QdUpDownSlab();
                upDownSlab.Host = host;
                upDownSlab.ReadProperties(ref arr[i]);
                upDownSlabs.Add(upDownSlab);
            }
            return upDownSlabs;
        }
    }
}
